import { Route, ViewType } from '@/types';

import { config } from '@/config';
import got from '@/utils/got';
import { art } from '@/utils/render';
import { load } from 'cheerio';
import path from 'node:path';

export const route: Route = {
    path: '/trending/:since/:language/:spoken_language?',
    categories: ['programming'],
    example: '/github/trending/daily/javascript/en',
    view: ViewType.Notifications,
    parameters: {
        since: {
            description: 'time range',
            options: [
                {
                    value: 'daily',
                    label: 'Today',
                },
                {
                    value: 'weekly',
                    label: 'This week',
                },
                {
                    value: 'monthly',
                    label: 'This month',
                },
            ],
        },
        language: {
            description: "the feed language, available in [Trending page](https://github.com/trending/javascript?since=monthly) 's URL, don't filter option is `any`",
            default: 'any',
        },
        spoken_language: {
            description: "natural language, available in [Trending page](https://github.com/trending/javascript?since=monthly) 's URL",
        },
    },
    features: {
        requireConfig: [
            {
                name: 'GITHUB_ACCESS_TOKEN',
                description: '',
            },
        ],
        requirePuppeteer: false,
        antiCrawler: false,
        supportBT: false,
        supportPodcast: false,
        supportScihub: false,
    },
    radar: [
        {
            source: ['github.com/trending'],
            target: '/trending/:since',
        },
    ],
    name: 'Trending',
    maintainers: ['DIYgod', 'jameschensmith'],
    handler,
    url: 'github.com/trending',
};

async function handler(ctx) {
    const candidates = ['daily', 'weekly', 'monthly'];
    let since = ctx.req.param('since');
    let languageParam = ctx.req.param('language');

    if (!candidates.includes(since) && candidates.includes(languageParam)) {
        const temp = since;
        since = languageParam;
        languageParam = temp;
    }

    const language = languageParam === 'any' ? '' : languageParam;
    const spoken_language = ctx.req.param('spoken_language') ?? '';

    const trendingUrl = `https://github.com/trending/${encodeURIComponent(language)}?since=${since}&spoken_language_code=${spoken_language}`;
    const { data: trendingPage } = await got({
        method: 'get',
        url: trendingUrl,
        headers: {
            Referer: trendingUrl,
        },
    });
    const $ = load(trendingPage);

    const articles = $('article');

    const parseNumber = (value: string) => {
        const normalized = value.trim().replace(/,/g, '').toLowerCase();
        if (!normalized) {
            return 0;
        }
        if (normalized.endsWith('k')) {
            return Math.round(parseFloat(normalized.slice(0, -1)) * 1_000);
        }
        if (normalized.endsWith('m')) {
            return Math.round(parseFloat(normalized.slice(0, -1)) * 1_000_000);
        }
        const parsed = Number(normalized);
        return Number.isNaN(parsed) ? 0 : parsed;
    };

    const trendingRepos = articles
        .toArray()
        .map((item) => {
            const headerLink = $(item).find('h2 a').attr('href') || '';
            const [ownerPart, namePart] = headerLink.replace(/^\//, '').split('/');
            const owner = ownerPart?.trim() || $(item).find('h2 span.text-normal').text().trim().replace('/', '') || '';
            const name = namePart?.trim() || $(item).find('h2').text().split('/')[1]?.trim() || '';
            const description = $(item).find('p').text().trim();
            const languageText = $(item).find('[itemprop="programmingLanguage"]').text().trim();
            const starsText = $(item)
                .find('a[href$="/stargazers"]')
                .first()
                .text()
                .trim();
            const forksText = $(item)
                .find('a[href$="/network/members"]')
                .first()
                .text()
                .trim();
            const avatar = $(item).find('img.avatar')?.first()?.attr('src');

            return {
                name,
                owner,
                description,
                language: languageText || 'Unknown',
                stars: parseNumber(starsText),
                forks: parseNumber(forksText),
                cover: avatar ? (avatar.startsWith('http') ? avatar : `https:${avatar}`) : `https://github.com/${owner}.png`,
            };
        })
        .filter((repo) => repo.name && repo.owner);

    if (!config.github || !config.github.access_token) {
        return {
            title: $('title').text(),
            link: trendingUrl,
            item: trendingRepos.map((r) => ({
                title: `${r.owner}/${r.name}`,
                author: r.owner,
                description: art(path.join(__dirname, 'templates/trending-description.art'), {
                    cover: r.cover,
                    desc: r.description,
                    forks: r.forks,
                    lang: r.language,
                    stars: r.stars,
                }),
                link: `https://github.com/${r.owner}/${r.name}`,
            })),
        };
    }

    const { data: repoData } = await got({
        method: 'post',
        url: 'https://api.github.com/graphql',
        headers: {
            Authorization: `bearer ${config.github.access_token}`,
        },
        json: {
            query: `
            query {
            ${trendingRepos
                .map(
                    (repo, index) => `
                _${index}: repository(owner: "${repo.owner}", name: "${repo.name}") {
                    ...RepositoryFragment
                }
            `
                )
                .join('\n')}
            }

            fragment RepositoryFragment on Repository {
                description
                forkCount
                nameWithOwner
                openGraphImageUrl
                primaryLanguage {
                    name
                }
                stargazerCount
            }
            `,
        },
    });

    const repos = Object.values(repoData.data).map((repo) => {
        const found = trendingRepos.find((r) => `${r.owner}/${r.name}` === repo.nameWithOwner);
        return { ...found, ...repo };
    });

    return {
        title: $('title').text(),
        link: trendingUrl,
        item: repos.map((r) => {
            const nameWithOwner = r.nameWithOwner || `${r.owner}/${r.name}`;
            const owner = r.owner || nameWithOwner?.split('/')?.[0] || '';
            const cover = r.openGraphImageUrl || r.cover || (owner ? `https://github.com/${owner}.png` : undefined);
            return {
                title: nameWithOwner,
                author: owner,
                description: art(path.join(__dirname, 'templates/trending-description.art'), {
                    cover,
                    desc: r.description,
                    forks: r.forkCount ?? r.forks ?? 0,
                    lang: r.primaryLanguage?.name || r.language || 'Unknown',
                    stars: r.stargazerCount ?? r.stars ?? 0,
                }),
                link: `https://github.com/${nameWithOwner}`,
            };
        }),
    };
}
